package com.ssly.media.manage.util;


import com.ssly.media.manage.configuration.MediaProperties;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;

/**
 * 多线程下载 和 断点续传
 *
 * @author 杨斌.
 */
public class DownLoad {

    final static public int DOWNING = 1;
    final static public int DOWN_FINISH = 3;
    private URL url;
    private String savePath;  //下载文件存放目录
    private Integer curLength = 0;
    private Integer connectionLength;
    private String fileName;
    private String fileExt;
    private String saveName;
    private UUID fileUUID;
    private File downFile;

    private int downStatus;
    private int threadCount = 3;    //线程数量

    /**
     * 构造方法
     *
     * @param url         要下载文件的网络路径
     * @param savePath    保存下载文件的目录
     * @param threadCount 开启的线程数量,默认为 3
     */
    public DownLoad(URL url, String savePath, int threadCount) throws MalformedURLException {
        this.url = url;
        this.savePath = savePath;
        this.threadCount = threadCount;
        this.fileUUID = UUID.randomUUID();
        String urlFileName = url.getFile();
        this.fileName = urlFileName.substring(urlFileName.lastIndexOf("/") + 1);
        this.fileExt = urlFileName.substring(urlFileName.lastIndexOf(".") + 1);
        this.saveName = fileUUID + "." + fileExt;
    }

    public DownLoad(String url, String savePath, int threadCount) throws MalformedURLException {
        this(new URL(url), savePath, threadCount);
    }

    public synchronized Integer getCurLength() {
        return curLength;
    }

    public synchronized void incCurLength(Integer length) {
        curLength += length;
    }

    /**
     * 开始下载文件
     */
    public void start() throws Exception {
        //连接资源

        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setRequestProperty("Accept-Encoding", "identity");
        connection.setConnectTimeout(10000);

        int code = connection.getResponseCode();

        if (code == 200) {
            //获取资源大小


            connectionLength = connection.getContentLength();
            //在本地创建一个与资源同样大小的文件来占位
            File file = new File(savePath, saveName);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
            randomAccessFile.setLength(connectionLength);
            /*
             * 将下载任务分配给每个线程
             */
            int blockSize = connectionLength / threadCount;//计算每个线程理论上下载的数量.
            for (int threadId = 0; threadId < threadCount; threadId++) {//为每个线程分配任务
                int startIndex = threadId * blockSize; //线程开始下载的位置
                int endIndex = (threadId + 1) * blockSize - 1; //线程结束下载的位置
                if (threadId == (threadCount - 1)) {  //如果是最后一个线程,将剩下的文件全部交给这个线程完成
                    endIndex = connectionLength - 1;
                }

                new DownloadThread(threadId, startIndex, endIndex).start();//开启线程下载

            }
            randomAccessFile.close();
        }

    }

    //下载的线程
    private class DownloadThread extends Thread {

        private int threadId;
        private int startIndex;
        private int endIndex;

        DownloadThread(int threadId, int startIndex, int endIndex) {
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            System.out.println("线程" + threadId + "开始下载");
            try {
                //分段请求网络连接,分段将文件保存到本地.

                //加载下载位置的文件
                File downThreadFile = new File(savePath, saveName + ".dt_" + threadId);
                RandomAccessFile downThreadStream = null;
                if (downThreadFile.exists()) {//如果文件存在
                    downThreadStream = new RandomAccessFile(downThreadFile, "rwd");
                    String startIndex_str = downThreadStream.readLine();
                    if (null == startIndex_str || "".equals(startIndex_str)) {  //网友 imonHu 2017/5/22
                        this.startIndex = startIndex;
                    } else {
                        this.startIndex = Integer.parseInt(startIndex_str) - 1;//设置下载起点
                    }
                } else {
                    downThreadStream = new RandomAccessFile(downThreadFile, "rwd");
                }

                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(10000);

                //设置分段下载的头信息。  Range:做分段数据请求用的。格式: Range bytes=0-1024  或者 bytes:0-1024
                connection.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

                System.out.println("线程_" + threadId + "的下载起点是 " + startIndex + "  下载终点是: " + endIndex);

                if (connection.getResponseCode() == 206) {//200：请求全部资源成功， 206代表部分资源请求成功
                    InputStream inputStream = connection.getInputStream();//获取流
                    RandomAccessFile randomAccessFile = new RandomAccessFile(
                            new File(savePath, saveName), "rw");//获取前面已创建的文件.
                    randomAccessFile.seek(startIndex);//文件写入的开始位置.


                    /*
                     * 将网络流中的文件写入本地
                     */
                    byte[] buffer = new byte[4096];
                    int length = -1;
                    int total = 0;//记录本次下载文件的大小
                    while ((length = inputStream.read(buffer)) > 0) {
                        randomAccessFile.write(buffer, 0, length);
                        total += length;

                        incCurLength(length);

                        /*
                         * 将当前现在到的位置保存到文件中
                         */
                        downThreadStream.seek(0);
                        downThreadStream.write((startIndex + total + "").getBytes("UTF-8"));
                    }


                    downThreadStream.close();
                    inputStream.close();
                    randomAccessFile.close();
                    cleanTemp(downThreadFile);//删除临时文件
                    System.out.println("线程" + threadId + "下载完毕");
                } else {
                    System.out.println("响应码是" + connection.getResponseCode() + ". 服务器不支持多线程下载");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    //删除线程产生的临时文件
    private synchronized void cleanTemp(File file) {
        file.delete();
    }

    public File getDownFile() {
        return new File(savePath, saveName);
    }

    public String getFileName() {
        return fileName;
    }

    public String getFileExt() {
        return fileExt;
    }

    public UUID getFileUUID() {
        return fileUUID;
    }

    public String getSaveName() {
        return saveName;
    }

    public Integer getConnectionLength() {
        return connectionLength;
    }

    public static void main(String[] args) {
        try {
            new DownLoad("http://sw.bos.baidu.com/sw-search-sp/software/8ecb3fd19eb5a/QQ_9.0.2.23468_setup.exe", "E:\\Down", 4).start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

